﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Sinacor.Infra.Common.Exceptions.FaultContracts;
using Sinacor.Infra.Common.Validation;
using Sinacor.Infra.Common.Exceptions;

namespace Sinacor.Infra.Common
{
    /// <summary>
    /// Classe base para camada de Comunicação
    /// </summary>
    public abstract class BaseCommunication
    {
        #region Construtores
        /// <summary>
        /// Construtor da classe <see cref="BaseCommunication"/>
        /// </summary>
        public BaseCommunication()
        {
        }
        #endregion

        #region CommunicationError
        /// <summary>
        /// Evento disparado caso ocorra um erro na chamada de um serviço
        /// </summary>
        public event EventHandler<CommunicationErrorEventArgs> CommunicationError;

        /// <summary>
        /// Dispara o evento CommunicationError
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCommunicationError(CommunicationErrorEventArgs e)
        {
            if (CommunicationError != null)
                CommunicationError(this, e);
        }
        #endregion

        #region HandleFault
        /// <summary>
        /// Converte a fault gerada pelo serviço para uma SinacorException específica.
        /// </summary>
        /// <param name="ex">Exception Gerada na classe de comunicação</param>
        /// <example>Tipos de Exception tratadas: BusinessException, ValidationException, GeneralException, CommunicationException e Exception</example>
        protected void HandleFault(Exception ex)
        {
            this.HandleFault(ex, true);
        }

        /// <summary>
        /// Converte a fault gerada pelo serviço para uma SinacorException específica.
        /// </summary>
        /// <param name="ex">Exception Gerada na classe de comunicação</param>
        /// <param name="throwException">Indica se deve disparar a Exception tratada ou disparar o evento CommunicationError</param>
        /// <example>Tipos de Exception tratadas: BusinessException, ValidationException, GeneralException, CommunicationException e Exception</example>
        protected virtual void HandleFault(Exception ex, bool throwException)
        {
            if (ex == null)
                throw new ArgumentNullException("ex");

            Exception exception = null;
            BaseFault fault = null;

            if (ex is FaultException<BusinessFault>)
            {
                fault = ((FaultException<BusinessFault>)ex).Detail;
            }
            else if (ex is FaultException<ValidationFault>)
            {
                fault = ((FaultException<ValidationFault>)ex).Detail;
            }
            else if (ex is FaultException<InvalidSessionFault>)
            {
                fault = ((FaultException<InvalidSessionFault>)ex).Detail;
            }
            else if (ex is FaultException<LicensingFault>)
            {
                fault = ((FaultException<LicensingFault>)ex).Detail;
            }
            else if (ex is FaultException<AccessIsDeniedFault>)
            {
                fault = ((FaultException<AccessIsDeniedFault>)ex).Detail;
            }
            else if (ex is FaultException<GeneralFault>)
            {
                fault = ((FaultException<GeneralFault>)ex).Detail;
            }
            else if (ex is System.ServiceModel.CommunicationException || 
                     ex is System.TimeoutException)
            {
                exception = this.CreateCommunicationException(ex);
            }
            else
            {
                exception = ex;
            }

            if (fault != null)
            {
                exception = fault.CreateException();
            }

            if (throwException)
            {
                throw exception;
            }
            else
            {
                this.OnCommunicationError(new CommunicationErrorEventArgs(exception));
            }
        }
        #endregion

        #region CreateException

        protected abstract Exception CreateCommunicationException(Exception ex);
       
        #endregion
    }

    /// <summary>
    /// EventArgs do evento CommunicationError
    /// </summary>
    public class CommunicationErrorEventArgs : EventArgs
    {
        public CommunicationErrorEventArgs(Exception ex)
        {
            if (ex == null)
                throw new ArgumentNullException("ex");

            this.Exception = ex;
        }

        public Exception Exception { get; private set; }
    }
}
